
#include "../include/GPCompute.h"

#include <windows.h>
#include <libpngIO.h>

#define SCREEN_WIDTH		512		// texture is 512w
#define SCREEN_HEIGHT		256		// texture is 256h

#define TEXTURE_WIDTH		512
#define TEXTURE_HEIGHT		256

#define SLICES				64		// intel max 96 or else the Device-heap inner will crash ! 
#define STEP				(SCREEN_HEIGHT / SLICES)

static const float Config[] = {
	2.0f/(SCREEN_WIDTH-1), -2.0f/(SCREEN_HEIGHT-1), 0.0f, 0.0f, 
	-1.0f, 1.0f, 0.0f, 1.0f, 
	1.0f/(SCREEN_WIDTH-1),  1.0f/(SCREEN_HEIGHT-1), 0.5f/(SCREEN_WIDTH-1), 0.5f/(SCREEN_HEIGHT-1), 
	0.0f, 0.0f, 0.0f, 0.0f, 
};

void DoCounter( HWND hWnd ) {
	static DWORD t;
	static LONG count;
	DWORD currtime = GetTickCount();
	if ( t==0 ) t = currtime;
	if ( t + 1000 < currtime ) { static int i;
		static char wndtxt[260], strfps[260];
		if (i==0) { i=1; GetWindowTextA( hWnd, wndtxt, MAX_PATH ); }
		wsprintfA( strfps, "%s - FPS:%d", wndtxt, count*1000/(currtime-t) );
		SetWindowTextA( hWnd, strfps );
		t += 1000; count = 0;
	}
	++count;
}

HRESULT LoadShader( LPDWORD* pdwShader, const char* filename )
{
	HANDLE p, f;
	p = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( INVALID_HANDLE_VALUE == p ) {
		return E_FAIL;
	}
	f = CreateFileMappingA( p, NULL, PAGE_READONLY, 0, 0, NULL );
	if ( NULL == f ) {
		return E_FAIL;
	}
	*pdwShader = (LPDWORD)MapViewOfFile( f, FILE_MAP_READ, 0, 0, 0 );
	if ( NULL == *pdwShader ) {
		return E_FAIL;
	}
	CloseHandle( f );
	CloseHandle( p );
	return S_OK;
}
void UnloadShader( DWORD* pdwShader )
{
	UnmapViewOfFile( (LPCVOID)pdwShader );
}

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
		case WM_TIMER:
			return 0;

        case WM_DESTROY:
            PostQuitMessage( 0 );
            return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE _1, LPSTR _2, INT cmdShow )
{
	HGPGPU hgpgpu;
	HWND hWnd; GP_CONTEXT ctx; RECT rect = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, };

	// Register the window class
    WNDCLASSEXA wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, /*hInst*/NULL, NULL, NULL, NULL, NULL,
                      "GP Tutorial", NULL };
	(void*)(_1, _2);
	wc.hInstance = hInst; 
    RegisterClassExA( &wc );

    // Create the application's window
    hWnd = CreateWindowA( "GP Tutorial", "GP06: Multiple",
                      WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
                      GetDesktopWindow(), NULL, wc.hInstance, NULL );

	// adjust window size ... 
	AdjustWindowRect( &rect, WS_OVERLAPPEDWINDOW, FALSE );
	MoveWindow( hWnd, 300+rect.left, 300+rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );

	// Prepare the context : 
	ctx.width = SCREEN_WIDTH;
	ctx.height = SCREEN_HEIGHT;
	ctx.colortype = TYPE_COLORALPHA;
	ctx.colorprecision = PREC_IN_32;
	ctx.depthtype = TYPE_DEPTH;
	ctx.depthprecision = PREC_IN_16;
	ctx.canvas = (void*)hWnd;

    // Initialize Direct3D
    if( GpComputeInit( &hgpgpu, &ctx, 0 ) >= 0 )
    {
        MSG msg;

		int ret, i, j; PNGStruct pnginfo;
		HGPARRAY harray; void* data; LPBYTE pData, pSrc; long pitch;
		HGPVRRAY hvrray; 
		DWORD vshid, pshid; LPDWORD p;
		GP_CALCSTATE state = {0}; 
		HGPSHAPERECT vrc;

		GP_RECT *rects; // n rectangles ... 
		GP_RECT *trects;
		float *fpos;

		rects = (GP_RECT *)malloc( sizeof(GP_RECT) * SLICES );
		trects = (GP_RECT *)malloc( sizeof(GP_RECT) * SLICES );
		fpos = (float *)malloc( sizeof(float) * 4*SLICES );

		// vertex prepare 
		for ( i=0; i<SLICES; ++i ) {
			rects[i].pt1.x = 0.0f, rects[i].pt1.y = (float)((i+0)*STEP);
			rects[i].pt2.x = (float)SCREEN_WIDTH, rects[i].pt2.y = (float)((i+1)*STEP);
			trects[i].pt1.x = 0.0f, trects[i].pt1.y = (float)((i+0)*STEP);
			trects[i].pt2.x = (float)TEXTURE_WIDTH, trects[i].pt2.y = (float)((i+1)*STEP);
/*			rects[i].pt1.x = -1.001f, rects[i].pt1.y = (float)(SCREEN_HEIGHT-2*(i+0)*STEP) / (SCREEN_HEIGHT-1);
			rects[i].pt2.x =  0.999f, rects[i].pt2.y = (float)(SCREEN_HEIGHT-2*(i+1)*STEP) / (SCREEN_HEIGHT-1);
			trects[i].pt1.x = 0.0f, trects[i].pt1.y = (float)((i+0)*STEP) / (TEXTURE_HEIGHT-1);
			trects[i].pt2.x = 1.0f, trects[i].pt2.y = (float)((i+1)*STEP) / (TEXTURE_HEIGHT-1);
*/
		}/**/
		ret = GpComputeShapeRectT( hgpgpu, rects, trects, SLICES, &vrc );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create Vertex Rectangle!\n" );
		ret = GpComputeSelectSourceShape( hgpgpu, vrc );
		if ( ret < 0 ) OutputDebugStringA( "Failed SetStreamSource and Index!\n" );

		// load resource 
		ret = LoadPNGFileA( "1.png", &pnginfo );
		if ( ret < 0 ) OutputDebugStringA( "Failed Load PNGFile!\n" );

		ret = GpComputeAllocate( hgpgpu, pnginfo.width, pnginfo.height, 
			TYPE_COLORALPHA, PREC_IN_32, OPMODE_READ, &harray );
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate Memory!\n" );

		ret = GpComputeGetPointer( harray, &data, &pitch );
		if ( ret < 0 ) OutputDebugStringA( "Failed GetPointer!\n" );

		// copy data to array 
		pData = (LPBYTE)data;
		pSrc = pnginfo.pData;
		for ( i=0; i<pnginfo.height; ++i ) {
		//	memcpy( pData, pSrc, pnginfo.width );
		//	pSrc += pnginfo.width; pData += pitch;
			for ( j=0; j<pnginfo.width; ++j ) {
				unsigned long n = *((DWORD*)pSrc+j);
			//	A R G B				 // A B G R
				*((DWORD*)pData+j) = ((n&0xFF000000))|((n&0x00FF0000)>>16)|((n&0x0000FF00))|((n&0x000000FF)<<16);
			/*	*((DWORD*)pData+j) = (0xFF<<24)|(j<<16)|(j<<8)|j, ++j;
				*((DWORD*)pData+j) = (0xFF<<24)|(j<<16)|(j<<8)|j, ++j;
				*((DWORD*)pData+j) = (0xFF<<24)|(j<<16)|(j<<8)|j, ++j;
				*((DWORD*)pData+j) = (0xFF<<24)|(j<<16)|(j<<8)|j, ++j;*/
			}
			pSrc += pnginfo.width*sizeof(DWORD), pData += pitch;
		}
		FreePNGFile( &pnginfo );

		// texture prepare 
		ret = GpComputeAllocateVram( hgpgpu, TEXTURE_WIDTH, TEXTURE_HEIGHT, 
			TYPE_COLORALPHA, PREC_IN_32, OPMODE_READ, &hvrray );
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate VideoMemory 1!\n" );
		ret = GpComputeSelectSourceUnit( hgpgpu, hvrray, 0 );
		if ( ret < 0 ) OutputDebugStringA( "Failed SetTexture!\n" );

		ret = GpComputeSubmit( hgpgpu, harray, hvrray, 0, 0 );
		if ( ret < 0 ) OutputDebugStringA( "Failed Submit VideoMemory!\n" );
		ret = GpComputeSubmit( hgpgpu, harray, hvrray, 256, 0 );
		if ( ret < 0 ) OutputDebugStringA( "Failed Submit VideoMemory!\n" );

		// shader programe 
		if (FAILED(LoadShader( &p, "GPComputeMultiple.vso" ))) 
			OutputDebugStringA( "Failed Allocate Load VertexShader!\n" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_VERTEX, &vshid, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create VertexShader!\n" );
		UnloadShader( p );
		ret = GpComputeSelectProgram( hgpgpu, PROGRAM_VERTEX, vshid );
		if ( ret < 0 ) OutputDebugStringA( "Failed Select VertexShader!\n" );
		ret = GpComputeSetConstants( hgpgpu, PROGRAM_VERTEX, 0, Config, 4 ); // pos 
		if ( ret < 0 ) OutputDebugStringA( "Failed Select VertexShader Constants!\n" );

		if (FAILED(LoadShader( &p, "GPComputeMultiple.pso" )))
			OutputDebugStringA( "Failed Allocate Load PixelShader" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_PIXEL, &pshid, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create PixelShader!\n" );
		UnloadShader( p );
		ret = GpComputeSelectProgram( hgpgpu, PROGRAM_PIXEL, pshid );
		if ( ret < 0 ) OutputDebugStringA( "Failed Select PixelShader!\n" );

		// render state : 
		state.red_write = state.green_write = state.blue_write = 1;
		ret = GpComputeSetProp( hgpgpu, &state );
		if ( ret < 0 ) OutputDebugStringA( "Failed Set RenderState!\n" );

		// Show the window
		ShowWindow( hWnd, cmdShow );
		UpdateWindow( hWnd );

        // Enter the message loop
        ZeroMemory( &msg, sizeof(msg) );

		SetTimer( hWnd, 0, 100, NULL );
        while( msg.message!=WM_QUIT )
        {
            if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
            else
            {
				GP_PREPARE prepare = {0};
				DoCounter( hWnd );
				// change fetch region : 
				for ( i=0; i<SLICES; ++i ) {
					fpos[4*i+0] = fpos[4*i+1] = 1.0f; // scale
					fpos[4*i+2] = fpos[4*i+3] = 0.0f; // offset
				}
				ret = GpComputeSetConstants( hgpgpu, PROGRAM_VERTEX, 4, fpos, SLICES ); // pos 
				if ( ret < 0 ) OutputDebugStringA( "Failed SetVertexContants!\n" );

				// RENDER 
				ret = GpComputeBeginTarget( hgpgpu, NULL, &prepare );
				if ( ret < 0 ) OutputDebugStringA( "Failed BeginScene!\n" );
				ret = GpComputeCalculateRect( hgpgpu, 0, 0, NULL ); // draw all 
				if ( ret < 0 ) OutputDebugStringA( "Failed RenderToTexture!\n" );
				ret = GpComputeEndTarget( hgpgpu, 1 ); // with present
				if ( ret < 0 ) OutputDebugStringA( "Failed EndScene!\n" );
            }
        }
		KillTimer( hWnd, 0 );

		GpComputeDeleteProgram( hgpgpu, PROGRAM_PIXEL, pshid );
		GpComputeDeleteProgram( hgpgpu, PROGRAM_VERTEX, vshid );

		GpComputeDeallocateVram( hgpgpu, hvrray );
		GpComputeDeallocate( hgpgpu, harray );

		GpComputeShapeFree( hgpgpu, vrc );

		free( fpos );
		free( trects );
		free( rects );

		GpComputeFree( hgpgpu );
    }

	DestroyWindow( hWnd );
    // Clean up everything and exit the app
    UnregisterClassA( "GP Tutorial", wc.hInstance );
    return 0;
}
